//
//  HWRequestLogManage
//
//  Created by 李含文 on 2021/4/28.
//  Copyright © 2021 MINISO. All rights reserved.
//

#import "HWHttpProtocol.h"
#import "HWLogViewModel.h"
#import "HWRequestLogManage.h"

#define k_HWProtocolKey @"k_HWProtocolKey"

IMP replaceMethod(SEL selector, IMP newImpl, Class affectedClass, BOOL isClassMethod) {
    Method origMethod = isClassMethod ? class_getClassMethod(affectedClass, selector) : class_getInstanceMethod(affectedClass, selector);
    IMP origImpl = method_getImplementation(origMethod);
    if (!class_addMethod(isClassMethod ? object_getClass(affectedClass) : affectedClass, selector, newImpl, method_getTypeEncoding(origMethod)))
        method_setImplementation(origMethod, newImpl);
    return origImpl;
}


typedef NSURLSessionConfiguration*(*SessionConfigConstructor)(id,SEL);
static SessionConfigConstructor orig_defaultSessionConfiguration;

static NSURLSessionConfiguration* replaced_defaultSessionConfiguration(id self, SEL _cmd) {
    // call original method
    NSURLSessionConfiguration* config = orig_defaultSessionConfiguration(self,_cmd);
    if (   [config respondsToSelector:@selector(protocolClasses)]
        && [config respondsToSelector:@selector(setProtocolClasses:)]){
        NSMutableArray * urlProtocolClasses = [NSMutableArray arrayWithArray:config.protocolClasses];
        Class protoCls = HWHttpProtocol.class;
        if (![urlProtocolClasses containsObject:protoCls]){
            [urlProtocolClasses insertObject:protoCls atIndex:0];
        }
        config.protocolClasses = urlProtocolClasses;
    }
    return config;
}

/// https://developer.aliyun.com/article/543412
@interface NSURLRequest (HWNSURLProtocolExtension)

- (NSMutableURLRequest *)hw_getPostRequestIncludeBody;

@end

@implementation NSURLRequest (HWNSURLProtocolExtension)

- (NSMutableURLRequest *)hw_getPostRequestIncludeBody {
    return [[self hw_getMutablePostRequestIncludeBody] mutableCopy];
}

- (NSMutableURLRequest *)hw_getMutablePostRequestIncludeBody {
    NSMutableURLRequest * req = [self mutableCopy];
    if ([self.HTTPMethod isEqualToString:@"POST"]) {
        if (!self.HTTPBody) {
            NSInteger maxLength = 1024;
            uint8_t d[maxLength];
            NSInputStream *stream = self.HTTPBodyStream;
            NSMutableData *data = [[NSMutableData alloc] init];
            [stream open];
            BOOL endOfStreamReached = NO;
            //不能用 [stream hasBytesAvailable]) 判断，处理图片文件的时候这里的[stream hasBytesAvailable]会始终返回YES，导致在while里面死循环。
            while (!endOfStreamReached) {
                NSInteger bytesRead = [stream read:d maxLength:maxLength];
                if (bytesRead == 0)  //文件读取到最后
                    endOfStreamReached = YES;
                else if (bytesRead == -1)  //文件读取错误
                    endOfStreamReached = YES;
                else if (stream.streamError == nil)
                    [data appendBytes:(void *)d length:bytesRead];
            }
            req.HTTPBody = [data copy];
            [stream close];
        }
    }
    return req;
}

@end

@interface HWHttpProtocol()<NSURLConnectionDelegate, NSURLConnectionDataDelegate>
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSURLResponse *response;
@property (nonatomic, strong) NSMutableData *data;
@property (nonatomic, strong) NSError *error;
@property (nonatomic, assign) NSTimeInterval startTime;
@end

@implementation HWHttpProtocol

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        orig_defaultSessionConfiguration = (SessionConfigConstructor)replaceMethod(@selector(defaultSessionConfiguration), (IMP)replaced_defaultSessionConfiguration, [NSURLSessionConfiguration class], YES);
    });
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    if (![request.URL.scheme isEqualToString:@"http"] && ![request.URL.scheme isEqualToString:@"https"])
        return NO;
    
    if ([NSURLProtocol propertyForKey:k_HWProtocolKey inRequest:request])
        return NO;
    
    if ([[HWRequestLogManage shared] onlyURLs].count) {
        NSString* url = [request.URL.absoluteString lowercaseString];
        for (NSString* _url in [HWRequestLogManage shared].onlyURLs) {
            if ([url containsString:_url.lowercaseString])
                return YES;
        }
        return NO;
    }

    return YES;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    NSMutableURLRequest *mutableReqeust = [request hw_getPostRequestIncludeBody]; // 拦截 body 丢失问题处理(只有常用的POST请求)
    [NSURLProtocol setProperty:@YES forKey:k_HWProtocolKey inRequest:mutableReqeust];
    return [mutableReqeust copy];
}

- (void)startLoading {
    self.data = [NSMutableData data];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    self.connection = [[NSURLConnection alloc] initWithRequest:[[self class] canonicalRequestForRequest:self.request] delegate:self startImmediately:YES];
#pragma clang diagnostic pop
    self.startTime = [[NSDate date] timeIntervalSince1970];
}

- (void)stopLoading {
    NSMutableURLRequest *request = [self.request mutableCopy];
    [self.connection cancel];
    NSString *start_time = [NSString stringWithFormat:@"%f", self.startTime];
    NSString *end_time = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
    NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)self.response;
    NSString *statusCode = [NSString stringWithFormat:@"%ld", httpResponse.statusCode];
    id responseObject;
    if (self.data.length) {
        NSError * error = nil;
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:self.data options:NSJSONReadingMutableContainers error:&error];
        if (error)
            responseObject = [[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding];
        else
            responseObject = dic;
    }
    __block NSString *vc_nmae = @"";
    __block NSString *vc_title = @"";
    [HWRequestLogManage.shared getVisibleViewController:^(UIViewController * _Nonnull vc) {
        if (vc) {
            vc_nmae = NSStringFromClass(vc.class);
            hwlog_dispatch_main_queue(^{
                vc_title = vc.title;
            });
        }
    }];
    [[HWLogViewModel shared] addstart_time:start_time end_time:end_time vc_nmae:vc_nmae vc_title:vc_title status_code:statusCode request:request responseObject:responseObject error:self.error];
}


//MARK: - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [[self client] URLProtocol:self didFailWithError:error];
    self.error = error;
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection {
    return YES;
}

//MARK: - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
    self.response = response;
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [[self client] URLProtocol:self didLoadData:data];
    [self.data appendData:data];
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    return cachedResponse;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [[self client] URLProtocolDidFinishLoading:self];
}

@end

